package pl.edu.agh.logview.graph;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import pl.edu.agh.logview.configuration.Configuration;
import pl.edu.agh.logview.database.DbHelper;
import pl.edu.agh.logview.model.Agent;
import pl.edu.agh.logview.model.AgentEdge;
import pl.edu.agh.logview.model.EdgeType;
import pl.edu.agh.logview.model.EndingVertex;
import pl.edu.agh.logview.model.Message;
import pl.edu.agh.logview.model.MessageEdge;
import pl.edu.agh.logview.model.MessageVertex;
import pl.edu.agh.logview.model.State;
import pl.edu.agh.logview.model.Vertex;
import pl.edu.agh.logview.model.VertexType;

import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.stmt.PreparedQuery;

public class GraphManager2 {

	private DbHelper db;
	
	private Date fromDate = null;
	private Date toDate = null;
	private String regexp = "";
	
	private long space = 30;
	
	private long scale = 500;	// (ilosc milisekund na 1px) * 1000
								// 100: 1ms = 1px
								// 500:	1ms = 5px
	
	private boolean regexpChanged = true;
	private boolean dateChanged = true;
	
	private boolean fixedSpace = false;
	
	private boolean needRedraw = true;
	
	private List<Agent> agentVertices;
	private List<MessageVertex> messageVertices;

	private List<EndingVertex> endingVertices;
	private List<State> stateVertices;

	private List<AgentEdge> agentEdges;
	private List<MessageEdge> messageEdges;
	
	private List<Agent> newAgentVertices;
	private List<MessageVertex> newMessageVertices;
	private List<EndingVertex> newEndingVertices;
	private List<State> newStateVertices;
	private List<AgentEdge> newAgentEdges;
	private List<MessageEdge> newMessageEdges;

	
	public GraphManager2() {
		db = new DbHelper();
		agentVertices = new LinkedList<Agent>();
		messageVertices = new LinkedList<MessageVertex>();
		endingVertices = new LinkedList<EndingVertex>();
		stateVertices = new LinkedList<State>();
		newAgentVertices = new LinkedList<Agent>();
		newMessageVertices = new LinkedList<MessageVertex>();
		newEndingVertices = new LinkedList<EndingVertex>();
		newStateVertices = new LinkedList<State>();
		agentEdges = new LinkedList<AgentEdge>();
		newAgentEdges = new LinkedList<AgentEdge>();
		messageEdges = new LinkedList<MessageEdge>();
		newMessageEdges = new LinkedList<MessageEdge>();
	}


	private void clearLists() {
		newAgentEdges.clear();
		newAgentVertices.clear();
		newEndingVertices.clear();
		newMessageEdges.clear();
		newMessageVertices.clear();
		newStateVertices.clear();
	}
	
	private boolean containsAgentVertices(Agent a) {
		for (Agent agent : agentVertices) {
			if (agent.getName().equals(a.getName()))
				return true;
		}
		return false;
	}
	
	private boolean containsStateVertices(State a) {
		try {
			db.getAgentDao().update(a.getAgent());
		} catch (SQLException e1) {
			e1.printStackTrace();
		}
		for (State state : stateVertices) {
			try {
				db.getAgentDao().update(state.getAgent());
			} catch (SQLException e) {
				e.printStackTrace();
			}
			if (state.getAgent().getName().equals(a.getAgent().getName()) &&
					state.getActionTime().equals(a.getActionTime()))
				return true;
		}
		return false;
	}

	private boolean containsMessageVertices(MessageVertex a) {
		for (MessageVertex message : messageVertices) {
			if (message.getDate().equals(a.getDate()) &&
					message.getX() == a.getX())
				return true;
		}
		return false;
	}
	
	private boolean containsMessageEdge(MessageEdge a) {
		for (MessageEdge message : messageEdges) {
			if (message.from().getName().equals(a.from().getName())  &&
					message.from().getDate().equals(a.from().getDate()) &&
					message.getMessage().equals(a.getMessage()))
				return true;
		}
		return false;
	}
	
	public void refresh() {
		clearLists();
		try {
			//agents + states
			List<State> stateTmp = new LinkedList<State>();
			List<Agent> agentTmp = new LinkedList<Agent>();
			List<EndingVertex> endingTmp = new LinkedList<EndingVertex>();
			List<MessageVertex> msgVertexTmp = new LinkedList<MessageVertex>();

			List<AgentEdge> agentEdgesTmp = new LinkedList<AgentEdge>();
			List<MessageEdge> messageEdgesTmp = new LinkedList<MessageEdge>();
			
			Dao<Agent,String> agentDao = db.getAgentDao();
			Dao<State,Long> stateDao = db.getStateDao();
			Dao<Message,Long> messageDao = db.getMessageDao();
			
			PreparedQuery<Agent> agentQuery;
			if (!regexp.equals(""))
				agentQuery = agentDao.queryBuilder().orderBy("agent", true).where().like("agent", regexp).prepare();
			else {
				agentQuery = agentDao.queryBuilder().orderBy("agent", true).prepare();
			}
			int agentCnt = 0;
			for (Agent agent : agentDao.query(agentQuery)) {
				agent.setVertexType(VertexType.VERTEX_AGENT);
				agent.setX( Configuration.START_X + agentCnt*(Configuration.SPACE+agent.getVertexType().getWidth()) );
				agentCnt++;
				agent.setY(Configuration.START_Y);
				agentDao.update(agent);
				agentTmp.add(agent);
				if (!containsAgentVertices(agent)) {
					System.out.println("dodano agenta do newAgents");
					newAgentVertices.add(agent);
				}
				System.out.println("Agent added: " + agent.getName());
				
				//states
				PreparedQuery<State> stateQuery;
				if (fromDate!=null && toDate !=null)
					stateQuery = stateDao.queryBuilder().orderBy("actionTime", true).where().between("actionTime", fromDate, toDate).
					and().eq("agent_id", agent).prepare();
				else 
					stateQuery = stateDao.queryBuilder().orderBy("actionTime", true).where().eq("agent_id", agent).prepare();
					
				for (State state : stateDao.query(stateQuery)) {
					state.setVertexType(VertexType.VERTEX_STATE);
					state.setX(agent.getCenterX() - (int)(state.getVertexType().getWidth()/2));
					state.setDate(state.getActionTime());
					stateTmp.add(state);
					if (!containsStateVertices(state)) {
						newStateVertices.add(state);
						System.out.println("state added to newStates");
					}
					System.out.println("state added: " + state.getName() + state.getActionTime());
				}
				
				EndingVertex ending = new EndingVertex();
				ending.setX(agent.getCenterX());
				endingTmp.add(ending);
				newEndingVertices.add(ending);
				
				AgentEdge agentEndingEdge = new AgentEdge();
				agentEndingEdge.setEdgeType(EdgeType.EDGE_AGENT);
				agentEndingEdge.setFrom(agent);
				agentEndingEdge.setTo(ending);
				agentEndingEdge.setMessage("");
				agentEdgesTmp.add(agentEndingEdge);
				newAgentEdges.add(agentEndingEdge); 
			}
			
			//messages
			PreparedQuery<Message> messagequery;
			if (fromDate!=null && toDate!=null)
				messagequery = messageDao.queryBuilder().orderBy("actionTime", true).where().in("agentFrom_id", agentTmp).and().
					in("agentTo_id", agentTmp).and().between("actionTime", fromDate, toDate).prepare();
			else 	
				messagequery = messageDao.queryBuilder().orderBy("actionTime", true).where().in("agentFrom_id", agentTmp).and().
					in("agentTo_id", agentTmp).and().prepare();
			for(Message message : messageDao.query(messagequery)) {
//				messageDao.refresh(message);
				if (message.getActionTime() != null) {
					agentDao.refresh(message.getAgentFrom());
					agentDao.refresh(message.getAgentTo());
					MessageVertex vertexFrom = new MessageVertex();
					vertexFrom.setX( message.getAgentFrom().getCenterX() );
					vertexFrom.setDate( message.getActionTime() );
					MessageVertex vertexTo = new MessageVertex();
					vertexTo.setX( message.getAgentTo().getCenterX() );
					vertexTo.setDate( message.getActionTime() );
					if (!containsMessageVertices(vertexTo))
						newMessageVertices.add(vertexTo);
					if (!containsMessageVertices(vertexFrom))
						newMessageVertices.add(vertexFrom);
					msgVertexTmp.add(vertexTo);
					msgVertexTmp.add(vertexFrom);
					
					MessageEdge messageEdge = new MessageEdge();
					messageEdge.setFrom(vertexFrom);
					messageEdge.setTo(vertexTo);
					messageEdge.setOnClickMessage(message.getMessage());
					messageEdge.setMessage((new SimpleDateFormat("yy-MM-dd HH:mm:ss:SS").format(message.getActionTime())));
					messageEdgesTmp.add(messageEdge);
					if (!containsMessageEdge(messageEdge))
						newMessageEdges.add(messageEdge);
				}
			}
			
			agentVertices.clear();
			agentVertices.addAll(agentTmp);
			stateVertices.clear();
			stateVertices.addAll(stateTmp);
			endingVertices.clear();
			endingVertices.addAll(endingTmp);
			messageVertices.clear();
			messageVertices.addAll(msgVertexTmp);
			messageEdges.clear();
			messageEdges.addAll(messageEdgesTmp);
			agentEdges.clear();
			agentEdges.addAll(agentEdgesTmp);
	
			setY();
			
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	
	private void setY() {
		List<Vertex> vertices = new LinkedList<Vertex>();
		vertices.addAll(stateVertices);
		vertices.addAll(messageVertices);
		Collections.sort(vertices);
		Map<Long, Integer> map = new HashMap<Long, Integer>();
		if (vertices.size() > 0) {
			if (fixedSpace) {
				int y = Configuration.START_Y +  Configuration.SPACE + Configuration.VERTEX_AGENT_HEIGHT;
				for(Vertex v : vertices) {
					map.put(v.getDate().getTime(), y);
					y+=Configuration.SPACE;
				}
				for(Vertex v : vertices) {
					v.setY(map.get(v.getDate().getTime()));
				}
				for (Vertex v : endingVertices)
					v.setY(y);
			} else {
				int y_start = Configuration.START_Y +  Configuration.SPACE + Configuration.VERTEX_AGENT_HEIGHT; 
				int y = y_start;
				long first = vertices.get(0).getDate().getTime();
				for (Vertex vertex : vertices) {
					y = y_start + (int)( ( (vertex.getDate().getTime() - first) * 1000 ) / scale ) ;
					vertex.setY(y);
				}
				for (Vertex v : endingVertices)
					v.setY(y);
			}
		}
	}
	
	
	public List<EndingVertex> getEndingVertices() {
		return endingVertices;
	}


	public void setEndingVertices(List<EndingVertex> endingVertices) {
		this.endingVertices = endingVertices;
	}


	public List<MessageEdge> getMessageEdges() {
		return messageEdges;
	}


	public void setMessageEdges(List<MessageEdge> messageEdges) {
		this.messageEdges = messageEdges;
	}


	public List<Agent> getAgentVertices() {
		return agentVertices;
	}


	public List<MessageVertex> getMessageVertices() {
		return messageVertices;
	}


	public List<AgentEdge> getAgentEdges() {
		return agentEdges;
	}


	public List<Agent> getNewAgentVertices() {
		return newAgentVertices;
	}


	public List<MessageVertex> getNewMessageVertices() {
		return newMessageVertices;
	}


	public List<EndingVertex> getNewEndingVertices() {
		return newEndingVertices;
	}


	public List<State> getNewStateVertices() {
		return newStateVertices;
	}


	public List<AgentEdge> getNewAgentEdges() {
		return newAgentEdges;
	}


	public List<MessageEdge> getNewMessageEdges() {
		return newMessageEdges;
	}


	public Date getFromDate() {
		return fromDate;
	}
	
	public void setFromDate(Date fromDate) {
		if (this.fromDate != null)
			if (this.fromDate.getTime() != fromDate.getTime())
				dateChanged = true;
		this.fromDate = fromDate;
	}
	
	public Date getToDate() {
		return toDate;
	}
	
	public void setToDate(Date toDate) {
		if (this.toDate != null)
			if (this.toDate.getTime() != toDate.getTime())
				dateChanged = true;
		this.toDate = toDate;
	}
	
	public String getRegexp() {
		return regexp;
	}
	
	public void setRegexp(String regexp) {
		if (this.regexp != null) 
			if (!this.regexp.equals(regexp))
				regexpChanged = true;
		this.regexp = regexp;
	}
	
	public void resetDate() {
		this.fromDate = null;
		this.toDate = null;
	}
	
	public void resetRegexp() {
		this.regexp = "";
	}
	
	public List<State> getStateVertices() {
		return stateVertices;
	}

	public void setStateVertices(List<State> stateVertices) {
		this.stateVertices = stateVertices;
	}
	
	public long getScale() {
		return scale;
	}


	public void setScale(long scale) {
		this.scale = scale;
	}


	public boolean isFixedSpace() {
		return fixedSpace;
	}


	public void setFixedSpace(boolean fixedSpace) {
		this.fixedSpace = fixedSpace;
	}

	
}
